﻿using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Sortable
{
    public class SorterHyperLink : HyperLink
    {
        /// <summary>
        /// Gets or sets the descending prefix
        /// </summary>
        [Category("Sorting"), DefaultValue("-"), Description("The prefix of the sort expression in the query string when it is descending")]
        public string DescendingPrefix
        {
            get { return (string)this.ViewState["DescendingPrefix"] ?? "-"; }
            set { this.ViewState["DescendingPrefix"] = value; }
        }

        /// <summary>
        /// Gets or sets the sort direction
        /// If left empty (null) it will set to the opposite of the acutal sort direction
        /// </summary>
        [Category("Sorting"),Description("The sort direction used to sort the data source, if empty it will be determinated from the current sort direction")]
        public SortDirection? Direction
        {
            get { return (SortDirection?)this.ViewState["Direction"]; }
            set { this.ViewState["Direction"] = value; }
        }

        /// <summary>
        /// Gets or sets the sort expression
        /// </summary>
        [Category("Sorting"), DefaultValue(""), Description("The sort expression used to sort the data source")]
        public string Expression
        {
            get { return (string)this.ViewState["Expression"] ?? string.Empty; }
            set { this.ViewState["Expression"] = value; }
        }

        /// <summary>
        /// Gets or sets the name of the parameter for the query string
        /// </summary>
        [Category("Sorting"), DefaultValue(""), Description("The name of the query string for the current sort settings")]
        public string QueryStringField
        {
            get { return (string)this.ViewState["QueryStringField"] ?? string.Empty; }
            set { this.ViewState["QueryStringField"] = value; }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            //Getting the current sort expression and direction from the request cache
            var sort = (Tuple<string, SortDirection>)HttpContext.Current.Items[this.QueryStringField];
            if (sort == null)
            {
                //Getting the sort expression from the query string and setting the direction to ascending (default)
                var expr = this.Page.Request.QueryString[this.QueryStringField] ?? string.Empty;
                var dir = SortDirection.Ascending;
                //Checking if the sort expression starts with the descending prefix
                if (expr.StartsWith(this.DescendingPrefix))
                {
                    //Removing the descending prefix from the sort expression and setting the direction to descending
                    expr = expr.Substring(this.DescendingPrefix.Length);
                    dir = SortDirection.Descending;
                }
                //Adding the sort expression to the request cache so the other sorter hyper link don't have to do it again
                HttpContext.Current.Items[this.QueryStringField] = sort = new Tuple<string, SortDirection>(expr, dir);

                //Searching for a sortable container
                Control container = this;
                ISortableItemContainer isic = null;
                while (container != null && isic == null)
                {
                    container = container.NamingContainer;
                    isic = container as ISortableItemContainer ?? SortableItemContainerAdapter.GetAdapter(container);
                }

                if (isic != null)
                {
                    //Getting the actual sort expression and direction of the container
                    var se = isic.SortExpression;
                    var sd = isic.SortDirection;

                    //If the actual sort expression and direction of the container are different from the query string ...
                    if (sort.Item1 != se || sort.Item2 != sd)
                    {
                        //Getting the actual page index and page size from the IPageableItemContainer interface
                        var pi = 0;
                        var ps = 10;
                        var ipic = container as IPageableItemContainer;
                        if (ipic != null)
                        {
                            pi = ipic.StartRowIndex;
                            ps = ipic.MaximumRows;
                        }

                        //Setting the new sort expression and direction, this reset the current page index and page size
                        isic.Sort(sort.Item1, sort.Item2);

                        //Resetting the previous page index and page size 
                        if (ipic != null) ipic.SetPageProperties(pi, ps, false);
                    }
                }
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            //Cloning the current request query string 
            var qs = HttpUtility.ParseQueryString(this.Page.Request.QueryString.ToString());
            
            //If no sort direction is defined, we have to determine it from the current sort direction
            var dir = this.Direction;
            if (dir == null)
            {
                //Getting the current sort expression and direction from the request cache
                var sort = (Tuple<string, SortDirection>)HttpContext.Current.Items[this.QueryStringField];
                //The sort direction is always ascending, except when the current sort expression is equal to the sort expression 
                //of the hyperlink and the actual sort direction is already ascending, in this case it would be descending
                dir = sort.Item1 == this.Expression && sort.Item2 == SortDirection.Ascending ? SortDirection.Descending : SortDirection.Ascending;
            }

            //Setting the sort expression and direction in the query string
            qs[this.QueryStringField] = (dir.Value == SortDirection.Ascending ? string.Empty : this.DescendingPrefix) + this.Expression;
            
            //Setting the navigate url with the changed query string
            this.NavigateUrl = this.Page.Request.Path + "?" + qs.ToString();
        }
    }
}